package com.bing.common.aspect;

import com.bing.common.annotation.SpeedLimit;
import com.bing.common.constant.CacheKey;
import com.bing.common.enums.BaseErrorCodeEnum;
import com.bing.common.redis.IRedisService;
import com.bing.common.util.AssertBusUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Slf4j
@Aspect
@Component
@Order(999)
@Import(SpelExpressionParser.class)
public class SpeedLimitAspect {
    @Autowired
    private SpelExpressionParser spelExpressionParser;

    @Autowired
    private IRedisService redisService;

    @Pointcut("@annotation(com.heakey.common.annotation.SpeedLimit)")//切入点描述
    public void speedLimit() {
    }//切入点的名称

    @Before("speedLimit()")
    public void before(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        SpeedLimit speedLimit = method.getAnnotation(SpeedLimit.class);

        String methodKey = method.getDeclaringClass().getSimpleName() + "." + method.getName();
        String parseKey = parseKey(speedLimit.key(), methodSignature.getParameterNames(), joinPoint.getArgs());
        boolean flag = redisService.getLock(String.format(CacheKey.SPEED_LIMIT, methodKey, parseKey), speedLimit.effectiveTime());
        AssertBusUtils.isTrue(flag, BaseErrorCodeEnum.OPTION_BUSY);
    }

    /**
     * 解析key
     *
     * @param key
     * @param parameterNames
     * @param args
     * @return
     */
    private String parseKey(String key, String[] parameterNames, Object[] args) {
        EvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < args.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        return spelExpressionParser.parseExpression(key).getValue(context).toString();
    }
}
